using System;
using System.ComponentModel;

namespace TrueCode.Core.EditableEntity
{
  public class Editable< T > : IEditable< T > where T : IEntity< T >
  {
    #region Fields
    private EditableObjectState _state;
    private T _current;
    #endregion

    #region .ctors
    protected Editable( T entity, bool isNew )
    {
      _current = entity;

      if( !isNew )
        Original = _current.Copy();

      _state = isNew ? EditableObjectState.New : EditableObjectState.Original;
    }
    #endregion

    #region Protected
    protected void SetAsModified()
    {
      if( State == EditableObjectState.Deleted )
        throw new InvalidOperationException( "Can not modify deleted entity, use RejectChanges to undelete it" );

      if( State != EditableObjectState.New && State != EditableObjectState.Modified )
        State = EditableObjectState.Modified;
    }
    #endregion

    #region Implementation of IEditable<T>
    public T Original { get; private set; }

    
    public T Current
    {
      get { return _current; }
      set
      {
        _current = value;
        SetAsModified();
        PropertyChanged.Raise( this, null );
      }
    }

    public Guid Key
    {
      get { return Current.Key; }
    }

    public EditableObjectState State
    {
      get { return _state; }
      private set
      {
        if( _state != value )
        {
          _state = value;
          PropertyChanged.Raise( this, () => State );
        }
      }
    }

    public void AcceptChanges()
    {
      Original = Current.Copy();
      State = EditableObjectState.Original;
    }

    public void RejectChanges()
    {
      if( _state == EditableObjectState.New )
        throw new InvalidOperationException( "Can not reject changes of the New entity " );

      Current = Original.Copy();
      State = EditableObjectState.Original;
    }

    public void MarkDeleted()
    {
      if( _state == EditableObjectState.New )
        throw new InvalidOperationException( "Can not mark New entity as Deleted" );
      // If RejectChanges will be called, we should reject MarkDeleted change, so copy current to original
      Original = Current.Copy();
      State = EditableObjectState.Deleted;

      OnMarkedDeleted();
    }
    #endregion

    protected virtual void OnMarkedDeleted(){}

    #region Implementation of INotifyPropertyChanged
    public event PropertyChangedEventHandler PropertyChanged;

    protected PropertyChangedEventHandler PropertyChangedEvent
    {
      get{ return PropertyChanged; }
    }
    #endregion
  }
}